//
// Created by 32827 on 2024/1/26.
//
#include "yzl_mem.h"
#define YZL_MEM_ERROR_INDEX 0xFFFF
YZL_MEM_BLOCK yzl_mem_blocks[YZ_MEM_BLOCK_MAX_COUNT];
int16_t yzl_mem_block_soft[YZ_MEM_BLOCK_MAX_COUNT];
int16_t yzl_mem_block_count=0;
YZ_MemManger *yzl_mem_manger;
int16_t yzl_mem_get_empty_block_index(){
    for (int16_t i = 0; i < YZ_MEM_BLOCK_MAX_COUNT; i++) {
        if(yzl_mem_blocks[i].baseAddress==0){
            return i;
        }
    }
    return YZL_MEM_ERROR_INDEX;
}

void yzl_mem_init(YZ_MemManger* pMemManger){
    yzl_mem_manger=pMemManger;
    memset(yzl_mem_blocks,0,sizeof(yzl_mem_blocks));
    memset(yzl_mem_block_soft,0,sizeof(yzl_mem_block_soft));
    yzl_mem_manger->pMalloc=yzl_mem_malloc;
    yzl_mem_manger->pFree=yzl_mem_free;
}
//TODO: need fix.
void* yzl_mem_malloc(size_t size){
    int16_t index=yzl_mem_get_empty_block_index();
    if(index==(int16_t)YZL_MEM_ERROR_INDEX){
        return NULL_PTR;
    }
    if(yzl_mem_block_count==0){
        yzl_mem_blocks[index].baseAddress=yzl_mem_manger->baseAddress;
        yzl_mem_blocks[index].endAddress=yzl_mem_blocks[index].baseAddress+size;
        yzl_mem_block_soft[yzl_mem_block_count]=index;
        yzl_mem_block_count++;
        return (void *)yzl_mem_blocks[index].baseAddress;
    }else{
        if(yzl_mem_manger->baseAddress+yzl_mem_manger->size-yzl_mem_blocks[yzl_mem_block_soft[yzl_mem_block_count-1]].endAddress>=size){
            yzl_mem_blocks[index].baseAddress=yzl_mem_blocks[yzl_mem_block_soft[yzl_mem_block_count-1]].endAddress;
            yzl_mem_blocks[index].endAddress=yzl_mem_blocks[index].baseAddress+size;
            yzl_mem_block_soft[yzl_mem_block_count]=index;
            yzl_mem_block_count++;
            return (void *)yzl_mem_blocks[index].baseAddress;
        }
        yzl_mem_zip();
        if(yzl_mem_manger->baseAddress+yzl_mem_manger->size-yzl_mem_blocks[yzl_mem_block_soft[yzl_mem_block_count-1]].endAddress>=size){
            yzl_mem_blocks[index].baseAddress=yzl_mem_blocks[yzl_mem_block_soft[yzl_mem_block_count-1]].endAddress;
            yzl_mem_blocks[index].endAddress=yzl_mem_blocks[index].baseAddress+size;
            yzl_mem_block_soft[yzl_mem_block_count]=index;
            yzl_mem_block_count++;
            return (void *)yzl_mem_blocks[index].baseAddress;
        }
        return NULL_PTR;
    }
}

void yzl_mem_free(void* ptr){
    for(int i=0;i<yzl_mem_block_count;i++){
        if(yzl_mem_blocks[yzl_mem_block_soft[i]].baseAddress<=(uint32_t)ptr&&yzl_mem_blocks[yzl_mem_block_soft[i]].endAddress-1>=(uint32_t)ptr){
            yzl_mem_blocks[yzl_mem_block_soft[i]].baseAddress=0;
            yzl_mem_blocks[yzl_mem_block_soft[i]].endAddress=0;
            if(i!=yzl_mem_block_count-1){
                memcpy(yzl_mem_block_soft+i,&yzl_mem_block_soft+i+1,sizeof(yzl_mem_block_soft)*(yzl_mem_block_count-i-1));
                yzl_mem_block_count--;
                yzl_mem_block_soft[yzl_mem_block_count]=0;
            }else{
                yzl_mem_block_count--;
                yzl_mem_block_soft[yzl_mem_block_count]=0;
            }
        }
    }
}
void yzl_mem_zip(){
    if(yzl_mem_blocks[yzl_mem_block_soft[0]].baseAddress!=yzl_mem_manger->baseAddress){
        int32_t size=yzl_mem_blocks[yzl_mem_block_soft[0]].endAddress-yzl_mem_blocks[yzl_mem_block_soft[0]].baseAddress;
        memcpy((void *)yzl_mem_manger->baseAddress,(void *)yzl_mem_blocks[yzl_mem_block_soft[0]].baseAddress,size);
        yzl_mem_blocks[yzl_mem_block_soft[0]].baseAddress=yzl_mem_manger->baseAddress;
        yzl_mem_blocks[yzl_mem_block_soft[0]].endAddress=yzl_mem_blocks[yzl_mem_block_soft[0]].baseAddress+size;
    }
    for(int i=1;i<yzl_mem_block_count;i++){
        if(yzl_mem_blocks[yzl_mem_block_soft[i-1]].endAddress!=yzl_mem_blocks[yzl_mem_block_soft[i]].baseAddress){
            //size = end - base
            int32_t size=yzl_mem_blocks[yzl_mem_block_soft[i]].endAddress-yzl_mem_blocks[yzl_mem_block_soft[i]].baseAddress;
            //move data
            memcpy((void *)yzl_mem_blocks[yzl_mem_block_soft[i-1]].endAddress,(void *)yzl_mem_blocks[yzl_mem_block_soft[i]].baseAddress,size);
            //end -> base
            yzl_mem_blocks[yzl_mem_block_soft[i]].baseAddress=yzl_mem_blocks[yzl_mem_block_soft[i-1]].endAddress;
            //relink
            yzl_mem_blocks[yzl_mem_block_soft[i]].endAddress=yzl_mem_blocks[yzl_mem_block_soft[0]].baseAddress+size;
        }
    }
}